using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Globalization;

namespace Microsoft.Cryptography
{
    /// <summary>
    /// Byte Manipulation Utilites.
    /// </summary>
    public class ByteTools
    {
        #region calcfunctions
        /// <summary>Calc the sum of a byte array</summary>
        /// <param name="bar">the array</param>
        /// <returns>The sum</returns>
        static public uint CalcSum(byte[] bar)
        {
            return CalcSum(bar.Length, bar);
        }
        /// <summary>
        /// Calc the sum over part of a byte array
        /// </summary>
        /// <param name="nin">How many to use from the begining</param>
        /// <param name="bar">The array</param>
        /// <returns>The sum</returns>
        static public uint CalcSum(int nin, byte[] bar)
        {
            uint sum = 0;
            for (int i = 0; i < nin; i++)
            {
                sum += bar[i];
            }
            return sum;
        }
        /// <summary>
        /// Calc the XOR value of a byte array
        /// </summary>
        /// <param name="bar">The byte array</param>
        /// <returns>The XOR of the array</returns>
        static public uint CalcXor(byte[] bar)
        {
            return CalcXor(bar.Length, bar);
        }
        /// <summary>
        /// Calc the XOR value of part of a byte array
        /// </summary>
        /// <param name="bar">The byte array</param>
        /// <param name="nin">The number of bytes to use</param>
        /// <returns>The XOR of the array</returns>
        static public uint CalcXor(int nin, byte[] bar)
        {
            uint[] x = new uint[4];
            for (int i = 0; i < nin; i++)
            {
                x[i % 4] ^= bar[i];
            }
            uint xor = ((x[0] << 24) + (x[1] << 16) + (x[2] << 8) + x[3]);
            return (uint)xor;
        }
        #endregion
        /// <summary>
        /// Format a byte
        /// </summary>
        /// <param name="b">The byte</param>
        /// <returns>The formated byte</returns>
        static string Fmtbyte(byte b)
        {
            string rv;
            //			if (b<=9) rv = "0x0"+b.ToString("x");
            //			else rv = "0x"+b.ToString("x");
            rv = b.ToString("x2");
            return rv;
        }
        /// <summary>
        /// Format (in C#) a byte array for output for use in a program
        /// </summary>
        /// <param name="name">The "printed" name of the array</param>
        /// <param name="bar">The byte array</param>
        /// <returns>The formated string, su</returns>
        static public string Outfmt(string name, byte[] bar)
        {
            return Outfmt(name, 0, bar.Length, bar);
        }
        /// <summary>
        /// Format (in C#) a part of a byte array for output for use in a program
        /// </summary>
        /// <param name="name">The "printed" name of the array</param>
        /// <param name="bar">The byte array</param>
        /// <param name="n">The number of bytes to format</param>
        /// <param name="start">The starting byte from which to format</param>
        /// <returns>The formated string</returns>
        static public string Outfmt(string name, int start, int n, byte[] bar)
        {
            string rv = "byte [] " + name + "_bar = {\n";
            for (int i = 0; i < n; i++)
            {
                rv += " 0x" + Fmtbyte(bar[i]) + ",";
                if ((i % 15) == 14) rv += "\n";
            }
            rv += "\n};\n";
            rv += "FlexInt " + name + " = new FlexInt( " + name + "_bar );";
            return rv;
        }
        /// <summary>
        /// Clean out new lines from a pem string
        /// </summary>
        /// <param name="instr">The input string</param>
        /// <returns>A clean string</returns>
        static public string CleanPemString(string instr)
        {
            string rv = "";
            for (int i = 0; i < instr.Length; i++)
            {
                char c = instr[i];
                if (c == '!') c = '\x0a';
                rv += c;
            }
            return rv;
        }
        /// <summary>
        /// Copy a specific section of an array
        /// </summary>
        /// <param name="start">The starting position</param>
        /// <param name="n">The number of bytes</param>
        /// <param name="bar">The byte array</param>
        /// <returns>The specified section of the array</returns>
        public static byte[] CopySection(int start, int n, byte[] bar)
        {
            if ((start + n) > bar.Length) throw new ApplicationException("CutSection out of range to the right");
            if (start < 0) throw new ApplicationException("CutSection out of range to the left");
            byte[] newbees = new byte[n];
            for (int i = 0; i < n; i++)
            {
                newbees[i] = bar[start + i];
            }
            return newbees;
        }
        /// <summary>
        /// Reverse the order of bytes in a byte array
        /// </summary>
        /// <param name="bar">The byte array</param>
        /// <returns>The byte array reversed</returns>
        public static byte[] Reverse(byte[] bar)
        {
            int n = bar.Length;
            byte[] nbar = new byte[n];
            for (int i = 0; i < n; i++)
            {
                nbar[i] = bar[n - i - 1];
            }
            return nbar;
        }
        /// <summary>
        /// Trim the zeros from the end of a byte array
        /// </summary>
        /// <param name="bar">The byte array</param>
        /// <returns>The byte array without the zeros</returns>
        public static byte[] TrimZerosFromEnd(byte[] bar)
        {
            int lori = bar.Length - 1;
            int lst = lori;
            while (lst >= 0 && bar[lst] == 0) lst--;
            if (lst == lori) return bar;

            byte[] nbar = CopySection(0, lst + 1, bar);
            return nbar;
        }
        /// <summary>
        /// Trim the zeros from the front of a byte array
        /// </summary>
        /// <param name="bar">The byte array</param>
        /// <returns>The byte array without the zeros</returns>
        public static byte[] TrimZerosFromFront(byte[] bar)
        {
            if (bar == null) return null;
            int lori = bar.Length - 1;
            int fst = 0;
            while (fst <= lori && bar[fst] == 0) fst++;
            if (fst == 0) return bar;

            byte[] nbar = CopySection(fst, lori, bar);
            return nbar;
        }
        /// <summary>
        /// Create a byte array with a single value
        /// </summary>
        /// <param name="n">The size of the array to be created</param>
        /// <param name="stuff">The value with which to initialize all the bytes</param>
        /// <returns>The created byte array</returns>
        public static byte[] Stuff(int n, byte stuff)
        {
            byte[] bar = new byte[n];
            for (int i = 0; i < n; i++) bar[i] = stuff;
            return bar;
        }
        /// <summary>
        /// Create a byte array with all zeros (C# does this anyway)
        /// </summary>
        /// <param name="n">The size of the array to be created</param>
        /// <returns>The created byte array</returns>
        public static byte[] Zeros(int n)
        {
            return Stuff(n, 0);
        }
        /// <summary>
        /// Add zeros to the end of an array
        /// </summary>
        /// <param name="bar">The byte array</param>
        /// <param name="nAdd">The number of zeros to append</param>
        /// <returns>The resultant byte array</returns>
        public static byte[] AddZeros(byte[] bar, int nAdd)
        {
            byte[] nbar = new byte[bar.Length + nAdd];
            for (int i = 0; i < nbar.Length; i++)
            {
                byte nv = 0;
                if (i < bar.Length) nv = bar[i];
                nbar[i] = nv;
            }
            return nbar;
        }
        /// <summary>
        /// Create a new byte array, padded out with a single value
        /// </summary>
        /// <param name="n">The total length of the new array</param>
        /// <param name="stuff">The value to pad with</param>
        /// <param name="bar">The original array</param>
        /// <returns>The created array of lenght n</returns>
        public static byte[] PadTo(int n, byte stuff, byte[] bar)
        {
            int need = n - bar.Length;
            if (need <= 0) return bar;

            byte[] nbar = new byte[n];
            for (int i = 0; i < bar.Length; i++) nbar[i] = bar[i];
            for (int i = bar.Length; i < n; i++) nbar[i] = stuff;
            return nbar;
        }
        /// <summary>
        /// Split a byte array into two
        /// </summary>
        /// <param name="bar">The original array</param>
        /// <param name="n">The size of the first array</param>
        /// <param name="bor1">The first resultant array</param>
        /// <param name="bor2">The second resultant array</param>
        public static void SplitOutBytes(byte[] bar, int n, out byte[] bor1, out byte[] bor2)
        {
            int n0 = bar.Length;
            if (n >= n0)
            {
                bor1 = bar;
                bor2 = null;
                return;
            }
            int n1 = n;
            int n2 = n0 - n;
            bor1 = CopySection(0, n1, bar);
            bor2 = CopySection(n1, n2, bar);
        }
        /// <summary>
        /// Convert a byte array to a binhex string
        /// </summary>
        /// <param name="bar">The byte array</param>
        /// <param name="fmt">Specify uppercase binhex with a X</param>
        /// <returns>A binhex array representing the bytes</returns>
        public static string ToBinHex(byte[] bar, string fmt)
        {
            string s = "";
            for (int i = 0; i < bar.Length; i++)
            {
                string ss = bar[i].ToString(fmt);
                if (ss.Length < 2) ss = "0" + ss;
                s = s + ss;
            }
            return s;
        }
        /// <summary>
        /// Convert a binhex string to a byte array
        /// </summary>
        /// <param name="s">The binhex string</param>
        /// <returns>The resultant byte array</returns>
        public static byte[] FromBinHex(string s)
        {
            int need = s.Length / 2;
            byte[] bar = new byte[need];
            for (int i = 0; i < need; i++)
            {
                string ss = s.Substring(i * 2, 2);
                byte b = Byte.Parse(ss, NumberStyles.AllowHexSpecifier);
                bar[i] = b;
            }
            return bar;
        }
        /// <summary>
        /// Test a byte array for byte-wise equality
        /// </summary>
        /// <param name="b1">The first byte array</param>
        /// <param name="b2">The second byte array</param>
        /// <returns>true if the arrays have the same values.</returns>
        public static bool IsEq(byte[] b1, byte[] b2)
        {
            if (b1 == b2) return true;
            if (b1.Length != b2.Length) return false;
            for (int i = 0; i < b1.Length; i++)
            {
                if (b1[i] != b2[i]) return false;
            }
            return true;
        }
        /// <summary>
        /// Convert a byte array to a char array
        /// </summary>
        /// <param name="bar">The byte array</param>
        /// <returns>The char array</returns>
        public static char[] CvtToChr(byte[] bar)
        {
            if (bar == null) return null;
            char[] car = new char[bar.Length];
            for (int i = 0; i < bar.Length; i++)
            {
                car[i] = (char)bar[i];
            }
            return car;
        }
        /// <summary>
        /// Convert a byte array to a string
        /// </summary>
        /// <param name="bar">The byte array</param>
        /// <returns>The string</returns>
        public static string CvtToStr(byte[] bar)
        {
            string s = (new System.Text.ASCIIEncoding()).GetString(bar);
            return s;
        }
        /// <summary>
        /// Write out a byte array to a file
        /// </summary>
        /// <param name="fname">The filename</param>
        /// <param name="bar">The byte array</param>
        /// <returns>0 if successful</returns>
        public static int WriteFile(string fname, byte[] bar)
        {
            FileStream fs = File.OpenWrite(fname);
            fs.SetLength(0);
            BinaryWriter bw = new BinaryWriter(fs);
            bw.Write(bar);
            bw.Close();
            fs.Close();
            return 0;
        }
        /// <summary>
        /// Convert an ascii coded byte array to an unicode coded byte array
        /// </summary>
        /// <param name="bees">The ascii coded byte array</param>
        /// <returns>A unicode coded byte array</returns>
        public static byte[] UnicodeBytes(byte[] bees)
        {
            int need = bees.Length * 2;
            if (need <= 0) return null;

            byte[] newBees = new Byte[need];
            for (int i = 0; i < bees.Length; i++)
            {
                newBees[i * 2] = bees[i];
            }
            return newBees;
        }
        /// <summary>
        /// Convert a string to a unicoded coded byte array
        /// </summary>
        /// <param name="msg">The string</param>
        /// <returns>A unicode coded byte array</returns>
        public static byte[] UnicodeBytes(string msg)
        {
            byte[] bees = (new System.Text.ASCIIEncoding()).GetBytes(msg);
            int need = bees.Length * 2;
            if (need <= 0) return null;

            byte[] newBees = new Byte[need];
            for (int i = 0; i < bees.Length; i++)
            {
                newBees[i * 2] = bees[i];
            }
            return newBees;
        }
        /// <summary>
        /// Concatinate two byte arrays
        /// </summary>
        /// <param name="bar1">The first byte array</param>
        /// <param name="bar2">The second byte array</param>
        /// <returns>A byte array with both inside</returns>
        public static byte[] Concatinate(byte[] bar1, byte[] bar2)
        {
            if (bar1 == null && bar2 == null) return null;
            if (bar1 == null) return bar2;
            if (bar2 == null) return bar1;
            byte[] foo = new byte[bar1.Length + bar2.Length];
            for (int i = 0; i < bar1.Length; i++)
            {
                foo[i] = bar1[i];
            }
            for (int i = 0; i < bar2.Length; i++)
            {
                foo[i + bar1.Length] = bar2[i];
            }
            return foo;
        }

        /// <summary>
        /// Checks if two byte arrays are dientical
        /// </summary>
        /// <param name="bar1">First byte array</param>
        /// <param name="bar2">Second byte array</param>
        /// <returns>true, if both byte arrays are equal - otherwise false</returns>
        public static bool AreEqual(byte[] bar1, byte[] bar2)
        {
            if (bar1.Length != bar2.Length)
                return false;

            for (int i = 0; i < bar1.Length; i++)
            {
                if (bar1[i] != bar2[i])
                    return false;
            }

            return true;
        }
    }
}
